/**
 * Copyright (c) 2025 myons Inc. All rights reserved.
 */
package cn.zmwh.common.core.utils;


import cn.hutool.json.JSONUtil;
import com.maxmind.geoip2.DatabaseReader;
import com.maxmind.geoip2.model.CityResponse;
import lombok.extern.slf4j.Slf4j;
import org.lionsoul.ip2region.xdb.Searcher;

import java.io.File;
import java.net.InetAddress;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;


/**
 * @describe: 、
 * @author: dmzmwh 、
 * @time: 2025-07-11 15:56 、
 */
@Slf4j
public class IpLocationUtil {

    public static void main(String[] args) throws Exception {
        init("D:\\data\\ip\\ip2region.xdb", null, "D:\\data\\ip\\dbip.mmdb");

        System.out.println(JSONUtil.toJsonStr(IpLocationUtil.ipResolve("39.76.246.44")));
        System.out.println(JSONUtil.toJsonStr(IpLocationUtil.ipResolve("8.8.8.8")));
    }
    private static volatile Searcher ip2RegionSearcher;
    private static volatile DatabaseReader maxMindReader;
    private static volatile DatabaseReader dbIpReader;

    private static final ExecutorService closeExecutor = Executors.newSingleThreadExecutor();

    public static void init(String ip2regionPath, String maxmindPath, String dbipPath) throws Exception {
        if (ip2regionPath != null) ip2RegionSearcher = Searcher.newWithFileOnly(ip2regionPath);
        if (maxmindPath != null) maxMindReader = new DatabaseReader.Builder(new File(maxmindPath)).build();
        if (dbipPath != null) dbIpReader = new DatabaseReader.Builder(new File(dbipPath)).build();
    }

    public static IpInfo ipResolve(String ip) {
        try {
            // 1. 尝试 ip2region
            Searcher searcher = ip2RegionSearcher;
            if (searcher != null) {
                String regionStr = searcher.search(ip);
                if (regionStr != null && !regionStr.contains("0|0|0|")) {
                    String[] parts = regionStr.split("\\|");
                    return new IpInfo(ip,
                            parts.length > 0 ? parts[0] : "",
                            parts.length > 2 ? parts[2] : "",
                            parts.length > 3 ? parts[3] : "",
                            "ip2region");
                }
            }

            InetAddress ipAddr = InetAddress.getByName(ip);

            // 2. 尝试 MaxMind
            DatabaseReader mmReader = maxMindReader;
            if (mmReader != null) {
                CityResponse response = mmReader.city(ipAddr);
                if (response != null && response.getCountry() != null) {
                    return new IpInfo(ip,
                            response.getCountry().getName(),
                            response.getMostSpecificSubdivision().getName(),
                            response.getCity().getName(),
                            "maxmind");
                }
            }

            // 3. 尝试 DB-IP
            DatabaseReader dbip = dbIpReader;
            if (dbip != null) {
                CityResponse response = dbip.city(ipAddr);
                if (response != null && response.getCountry() != null) {
                    return new IpInfo(ip,
                            response.getCountry().getName(),
                            response.getMostSpecificSubdivision().getName(),
                            response.getCity().getName(),
                            "dbip");
                }
            }
        } catch (Exception e) {
            log.error("ip 解析异常", e);
        }
        return new IpInfo(ip, "未知", "未知", "", "error");
    }

    private static void asyncCloseIp2RegionSearcher(Searcher oldSearcher) {
        closeExecutor.submit(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
                oldSearcher.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }

    private static void asyncCloseReader(DatabaseReader oldReader) {
        closeExecutor.submit(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
                oldReader.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }

    public static synchronized void reloadIp2Region(String ip2regionPath) throws Exception {
        Searcher newSearcher = Searcher.newWithFileOnly(ip2regionPath);
        Searcher old = ip2RegionSearcher;
        ip2RegionSearcher = newSearcher;
        if (old != null) asyncCloseIp2RegionSearcher(old);
        log.info("ip2region 热更新成功");
    }

    public static synchronized void reloadMaxMind(String maxmindPath) throws Exception {
        DatabaseReader newReader = new DatabaseReader.Builder(new File(maxmindPath)).build();
        DatabaseReader old = maxMindReader;
        maxMindReader = newReader;
        if (old != null) asyncCloseReader(old);
        log.info("MaxMind 热更新成功");
    }

    public static synchronized void reloadDbIp(String dbipPath) throws Exception {
        DatabaseReader newReader = new DatabaseReader.Builder(new File(dbipPath)).build();
        DatabaseReader old = dbIpReader;
        dbIpReader = newReader;
        if (old != null) asyncCloseReader(old);
        log.info("DB-IP 热更新成功");
    }

    public static void close() throws Exception {
        if (ip2RegionSearcher != null) ip2RegionSearcher.close();
        if (maxMindReader != null) maxMindReader.close();
        if (dbIpReader != null) dbIpReader.close();
        closeExecutor.shutdown();
    }

}
